package com.stark.lock.aop;

import io.micrometer.common.util.StringUtils;
import lombok.Getter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import java.lang.reflect.Method;

/**
 * 作用于<strong>方法上</strong>的注解的 spel 表达式解析器。
 * <p>通过 {@link #getValue(String)} 获取解析值。
 *
 * @author Ben
 * @version 1.0.0
 * @since 1.0.0
 */
public class MethodExpressionEvaluator {

    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

    private final EvaluationContext evaluationContext;

    @Getter
    private final Class<?> targetClass;

    @Getter
    private final Method targetMethod;

    public MethodExpressionEvaluator(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        this.targetClass = getTargetClass(joinPoint.getTarget());
        this.targetMethod = getTargetMethod(targetClass, method);
        MethodExpressionRootObject rootObject = new MethodExpressionRootObject(
                targetMethod, joinPoint.getArgs(), joinPoint.getTarget(), targetClass);
        this.evaluationContext = new MethodBasedEvaluationContext(
                rootObject, method, joinPoint.getArgs(), new DefaultParameterNameDiscoverer());
    }

    /**
     * 解析表达式。
     *
     * @param expression 表达式。
     * @return 解析后的值。
     */
    public Object getValue(String expression) {
        if (StringUtils.isBlank(expression)) {
            return "";
        }

        return spelExpressionParser.parseExpression(expression).getValue(evaluationContext);
    }

    /**
     * 获取实际执行类。
     *
     * @param target 类对象。
     * @return 实际执行类。
     */
    private Class<?> getTargetClass(Object target) {
        return AopProxyUtils.ultimateTargetClass(target);
    }

    /**
     * 获取实际执行方法。
     *
     * @param targetClass 类。
     * @param method      方法。
     * @return 实际执行方法。
     */
    private Method getTargetMethod(Class<?> targetClass, Method method) {
        return AopUtils.getMostSpecificMethod(method, targetClass);
    }

}
